package fr.upmc.dtgui.reflexion;

import java.lang.reflect.Modifier;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;

/**
 * <b>RobotStateDataFactory est la classe Factory qui va créer les sous-classes
 * de RobotStateData, permettant la lecture de l'état du robot par la station de 
 * téléopération.</b>
 * <p>
 * Cette classe va créer deux sous-classes de RobotStateData via deux
 * méthodes différentes:
 * <ul>
 * <li>createEnergyData() va créer la classe EnergyData</li>
 * <li>createSpeedData() va créer la classe SpeedData</li>
 * <li>createSteeringData() va créer la classe SteeringData</li>
 * </ul>
 * </p>
 * 
 * @see fr.upmc.dtgui.robot.RobotStateData
 * 
 * @author Ayoub Benali, Maxime Sarilar
 */
public class RobotStateDataFactory {
	
	/**
     * La méthode createEnergyData.
     * <p>
     * Cette méthode se charge de créer lors du chargement du robot la classe
     * EnergyData qui permettra à la station de téléopération de lire l'énergie
     * du robot.
     * </p>
     * 
     * @param pool
     *            Le ClassPool associé à la classe chargée.
     * @param ct
     *            La CtClass du robot.
     * @return
     * 			  La CtClass EnergyData générée.
     */
	public static CtClass createEnergyData(CtClass ct, ClassPool pool)
			throws Exception {
		CtClass energyData = ct.makeNestedClass("EnergyData", true);
		energyData.setSuperclass(pool
				.getCtClass("fr.upmc.dtgui.robot.RobotStateData"));

		//Ajout du champ:
		CtField level = new CtField(CtClass.doubleType, "level", energyData);
		level.setModifiers(Modifier.PUBLIC);
		energyData.addField(level);

		//Ajout du constructeur:
		CtConstructor energyDataConstructor = new CtConstructor(
				new CtClass[] { CtClass.doubleType }, energyData);
		String energyDataConstructorString = "{" + "super();"
				+ "$0.level = $1;" + "}";
		energyDataConstructor.setBody(energyDataConstructorString);
		energyData.addConstructor(energyDataConstructor);
		energyData.setModifiers(Modifier.PUBLIC);
		
		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		energyData.toClass();
		return energyData;
	}
	
	public static CtClass createSpeedData(CtClass ct, ClassPool pool)
			throws Exception {
		CtClass speedData = ct.makeNestedClass("SpeedData", true);
		speedData.setSuperclass(pool
				.getCtClass("fr.upmc.dtgui.robot.RobotStateData"));

		//Ajout du champ:
		CtField speed = new CtField(CtClass.doubleType, "speed", speedData);
		speed.setModifiers(Modifier.PUBLIC);
		speedData.addField(speed);

		//Ajout du constructeur:
		CtConstructor speedConstructor = new CtConstructor(
				new CtClass[] { CtClass.doubleType }, speedData);
		speedConstructor.setBody("{super(); $0.speed = $1;}");
		speedData.addConstructor(speedConstructor);
		speedData.setModifiers(Modifier.PUBLIC);
		
		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		speedData.toClass();
		return speedData;
	}
	
	public static CtClass createSteeringData(CtClass ct, ClassPool pool)
			throws Exception {
		CtClass steeringData = ct.makeNestedClass("SteeringData", true);
		steeringData.setSuperclass(pool
				.getCtClass("fr.upmc.dtgui.robot.RobotStateData"));

		//Ajout du champ:
		CtField steeringAngle = new CtField(CtClass.doubleType,
				"steeringAngle", steeringData);
		steeringData.setModifiers(Modifier.PUBLIC);
		steeringData.addField(steeringAngle);

		/* Ajout d'un accesseur pour obtenir le champ, au contraire des autres
		 * RobotStateData. C'est le seul moyen que nous avont trouvé pour 
		 * résoudre un problème inconnu lié à Javassist.
		 */
		CtMethod get = CtNewMethod
				.make("public double getSteeringAngle() " +
						"{ return $0.steeringAngle; }",
						steeringData);
		steeringData.addMethod(get);

		//Ajout du constructeur:
		CtConstructor steeringDataConstructor = new CtConstructor(
				new CtClass[] { CtClass.doubleType }, steeringData);
		String steeringDataConstructorString = "{" + "super();"
				+ "$0.steeringAngle = $1;" + "}";
		steeringData.setModifiers(Modifier.PUBLIC);
		steeringDataConstructor.setBody(steeringDataConstructorString);
		steeringData.addConstructor(steeringDataConstructor);
		
		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		steeringData.toClass();
		return steeringData;
	}
}
